package cn.stylefeng.guns.config.redis;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;


/**
 * redis操作类
 */
public class RedisTemplateUtil {

    /**
     * 日志记录
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * redis基础操作类
     */
    private RedisTemplate redisTemplate;

    public RedisTemplateUtil(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 缓存String类型
     *
     * @param prefex 前缀
     * @param key    键
     * @param value  值
     * @return 是否成功
     */
    public boolean cacheStringValue(String prefex, String key, String value) {
        String keys = prefex + key;
        try {
            redisTemplate.boundValueOps(keys).set(value);
            return true;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return false;
    }

    /**
     * 缓存String类型，并设置超时时间
     *
     * @param prefex  前缀
     * @param key     键
     * @param value   值
     * @param timeout 超时时间
     * @return 是否成功
     */
    public boolean cacheStringValue(String prefex, String key, String value, long timeout) {
        String keys = prefex + key;
        try {
            redisTemplate.boundValueOps(keys).set(value, timeout, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return false;
    }

    /**
     * 获取缓存String类型
     *
     * @param prefex 前缀
     * @param key    键
     * @return 是否成功
     */
    public String getStringValue(String prefex, String key) {
        try {
            String keys = prefex + key;
            ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
            return valueOps.get(keys);
        } catch (Throwable t) {
            t.printStackTrace();
            return "";
        }
    }

    /**
     * 获取缓存StringList类型
     *
     * @param prefix  前缀
     * @param keyList 键集合
     * @return 是否成功
     */
    public List<String> getStringValueList(String prefix, List<String> keyList) {
        try {
            List<String> keysList = new ArrayList<>();
            keyList.stream().forEach(key -> keysList.add(prefix + key));
            ValueOperations<String, String> valueOps = redisTemplate.opsForValue();
            return valueOps.multiGet(keysList);
        } catch (Throwable t) {
            t.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 获取缓存Hash类型
     *
     * @param prefex 前缀
     * @param key    键
     * @return 是否成功
     */
    public Map<Object, Object> getHashMap(String prefex, String key) {
        try {
            String keys = prefex + key;
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            return hash.entries(keys);
        } catch (Throwable t) {
            t.printStackTrace();
            return null;
        }
    }

    /**
     * 获取缓存获取缓存Hash类型List类型
     *
     * @param prefix      前缀
     * @param key         键
     * @param hashKeyList hash键集合
     * @return 是否成功
     */
    public List<String> getHashList(String prefix, String key, List<String> hashKeyList) {
        try {
            String keys = prefix + key;
            HashOperations<String, String, String> hash = redisTemplate.opsForHash();
            return hash.multiGet(keys, hashKeyList);
        } catch (Throwable t) {
            t.printStackTrace();
            return new ArrayList<>();
        }
    }

    /**
     * 缓存list
     *
     * @param k
     * @param v
     * @return
     */
    public boolean cacheList(String PREFIX, String k, List<String> v) {
        return cacheList(PREFIX, k, v, -1);
    }

    /**
     * 缓存list
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheList(String PREFIX, String k, List<String> v, long time) {
        String key = PREFIX + k;
        try {
            ListOperations listOps = redisTemplate.opsForList();
            long l = listOps.rightPushAll(key, v);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * list缓存
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheList(String PREFIX, String k, String v, long time) {
        String key = PREFIX + k;
        try {
            ListOperations<String, String> listOps = redisTemplate.opsForList();
            listOps.rightPush(key, v);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存list
     *
     * @param k
     * @param v
     * @return
     */
    public boolean cacheList(String PREFIX, String k, String v) {
        return cacheList(PREFIX, k, v, -1);
    }

    /**
     * 获取list缓存
     *
     * @param k
     * @param start
     * @param end
     * @return
     */
    public List<String> getList(String PREFIX, String k, long start, long end) {
        try {
            ListOperations<String, String> listOps = redisTemplate.opsForList();
            return listOps.range(PREFIX + k, start, end);
        } catch (Throwable t) {
            logger.error("获取list缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 获取list缓存
     *
     * @param PREFIX
     * @param k
     * @return
     */
    public List<String> getList(String PREFIX, String k) {
        try {
            ListOperations<String, String> listOps = redisTemplate.opsForList();
            return listOps.range(PREFIX + k, 0, -1);
        } catch (Throwable t) {
            logger.error("获取list缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 移除并返回列表 key 的头元素。
     *
     * @param PREFIX
     * @param k
     * @return
     */
    public String getListLeftPop(String PREFIX, String k) {
        try {
            return redisTemplate.opsForList().leftPop(PREFIX + k).toString();
        } catch (Throwable t) {
            logger.error("获取list缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }


    /**
     * 获取总条数, 可用于分页
     *
     * @param k
     * @return
     */
    public long getListSize(String PREFIX, String k) {
        try {
            ListOperations<String, String> listOps = redisTemplate.opsForList();
            return listOps.size(PREFIX + k);
        } catch (Throwable t) {
            logger.error("获取list长度失败key[" + PREFIX + k + "], error[" + t + "]");
        }
        return 0;
    }

    /**
     * 获取总条数, 可用于分页
     *
     * @param listOps
     * @param k
     * @return
     */
    public long getListSize(String PREFIX, ListOperations<String, String> listOps, String k) {
        try {
            return listOps.size(PREFIX + k);
        } catch (Throwable t) {
            logger.error("获取list长度失败key[" + PREFIX + k + "], error[" + t + "]");
        }
        return 0;
    }

    /**
     * 移除list缓存
     *
     * @param k
     * @return
     */
    public boolean removeOneOfList(String PREFIX, String k) {
        String key = PREFIX + k;
        try {
            ListOperations<String, String> listOps = redisTemplate.opsForList();
            listOps.rightPop(PREFIX + k);
            return true;
        } catch (Throwable t) {
            logger.error("移除list缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 移除缓存
     *
     * @param key
     * @return
     */
    public boolean remove(String prefix, String key) {
        try {
            redisTemplate.delete(prefix + key);
            return true;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return false;
    }

    /**
     * 移除缓存
     *
     * @param keyList
     * @return
     */
    public boolean removeKeys(List<String> keyList) {
        try {
            redisTemplate.delete(keyList);
            return true;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return false;
    }


    /**
     * 缓存set操作
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheSet(String PREFIX, String k, String v, long time) {
        String key = PREFIX + k;
        try {
            SetOperations<String, String> valueOps = redisTemplate.opsForSet();
            valueOps.add(key, v);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存set
     *
     * @param k
     * @param v
     * @return
     */
    public boolean cacheSet(String PREFIX, String k, String v) {
        return cacheSet(PREFIX, k, v, -1);
    }

    /**
     * 缓存set
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheSet(String PREFIX, String k, Set<String> v, long time) {
        String key = PREFIX + k;
        try {
            SetOperations<String, String> setOps = redisTemplate.opsForSet();
            setOps.add(key, v.toArray(new String[v.size()]));
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存set
     *
     * @param k
     * @param v
     * @return
     */
    public boolean cacheSet(String PREFIX, String k, Set<String> v) {
        return cacheSet(PREFIX, k, v, -1);
    }

    /**
     * 获取缓存set数据
     *
     * @param k
     * @return
     */
    public Set<String> getSet(String PREFIX, String k) {
        try {
            SetOperations<String, String> setOps = redisTemplate.opsForSet();
            return setOps.members(PREFIX + k);
        } catch (Throwable t) {
            logger.error("获取set缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    public boolean removeHash(String PREFIX, String k, String hashKey) {
        String key = PREFIX + k;
        try {
            redisTemplate.opsForHash().delete(key, hashKey);
            return true;
        } catch (Throwable t) {
            logger.error("删除hash失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return false;
    }

    public boolean cacheHash(String PREFIX, String k, String hashKey, String value) {
        return cacheHash(PREFIX, k, hashKey, value, -1);
    }

    public boolean cacheHash(String PREFIX, String k, String hashKey, String value, long time) {
        String key = PREFIX + k;
        try {
            BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
            boundHashOps.put(hashKey, value);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("保存hash缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return false;
    }

    public boolean cacheHashAll(String PREFIX, String k, Map<String, String> hashKey, long time) {
        String key = PREFIX + k;
        try {
            BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
            boundHashOps.putAll(hashKey);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("保存hash缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return false;
    }

    public boolean cacheHashAll(String PREFIX, String k, Map<String, String> hashKey) {
        return cacheHashAll(PREFIX, k, hashKey, -1);
    }

    public String getHash(String PREFIX, String k, String hashKey) {
        String key = PREFIX + k;
        try {
            BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(key);
            return boundHashOps.get(hashKey);
        } catch (Throwable t) {
            logger.error("获取hash缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 缓存GEO多个member
     *
     * @param key 缓存key
     * @param map 多个点
     */
    public boolean cacheGeos(String key, Map<String, Point> map) {
        try {
            BoundGeoOperations boundGeoOperations = redisTemplate.boundGeoOps(key);
            long num = boundGeoOperations.add(map);
            if(num == map.size()){
                return true;
            }
        } catch (Throwable t) {
            logger.error("保存多个Geo缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 缓存GEO member
     *
     * @param key 缓存key
     * @param point 一个点
     * @param member 成员名称
     */
    public boolean cacheGeo(String key, Point point, String member) {
        try {
            BoundGeoOperations boundGeoOperations = redisTemplate.boundGeoOps(key);
            if(boundGeoOperations.add(point, member)  == 1) {
                return true;
            }
        } catch (Throwable t) {
            logger.error("保存Geo缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 删除GEO member
     *
     * @param key 缓存key
     * @param member 成员名称
     */
    public boolean removeGeo(String key, String member) {
        try {
            BoundGeoOperations boundGeoOperations = redisTemplate.boundGeoOps(key);
            if(boundGeoOperations.remove(member)  == 1) {
                return true;
            }
        } catch (Throwable t) {
            logger.error("删除Geo缓存失败key[" + key + ", error[" + t + "]");
        }
        return false;
    }

    /**
     * 获取距离某点point范围内distance的商家
     *
     * @param key 缓存key
     * @param point 一个点
     * @param distance 距离
     */
    public List geoRadius(String key, Point point, double distance) {
        try {
            GeoOperations geoOperations = redisTemplate.opsForGeo();
            GeoResults geoResult = geoOperations.radius(key, new Circle(point, new Distance(distance)));
            return geoResult.getContent();
        } catch (Throwable t) {
            logger.error("geoRadius缓存失败key[" + key + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 获取距离某点point范围内distance的商家，返回距离并排序
     *
     * @param key 缓存key
     * @param point 一个点
     * @param distance 距离
     */
    public List geoRadiusWithDistAndAsc(String key, Point point, double distance) {
        try {
            GeoOperations geoOperations = redisTemplate.opsForGeo();
            GeoResults geoResult = geoOperations.radius(key, new Circle(point, new Distance(distance)), RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending());
            return geoResult.getContent();
        } catch (Throwable t) {
            logger.error("geoRadiusWithDistAndAsc缓存失败key[" + key + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 范围检索
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<String> range(String key, int start, int end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 入队
     *
     * @param key
     * @param value
     * @return
     */
    public Long in(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 校验对应的key是否存在
     *
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }


    /**
     * 压栈
     *
     * @param PREFIX
     * @param value
     * @return
     */
    public Long push(String PREFIX, String k, String value) {
        String key = PREFIX + k;
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 左插入集合
     *
     * @param PREFIX
     * @param value
     * @return
     */
    public Long pushAll(String PREFIX, String k, List<Object> value) {
        String key = PREFIX + k;
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 出栈
     *
     * @param PREFIX
     * @return
     */
    public Object pop(String PREFIX, String k) {
        String key = PREFIX + k;
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 栈/队列长
     *
     * @return
     */
    public Long length(String PREFIX, String k) {
        String key = PREFIX + k;
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 取出集合
     *
     * @param PREFIX
     * @param k
     * @param start
     * @param end
     * @return
     */
    public List range(String PREFIX, String k, long start, long end) {
        String key = PREFIX + k;
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 缓存zSet操作
     *
     * @param k
     * @param v
     * @param time
     * @return
     */
    public boolean cacheZSet(String PREFIX, String k, String v, double score, long time) {
        String key = PREFIX + k;
        try {
            ZSetOperations<String, String> valueOps = redisTemplate.opsForZSet();
            valueOps.add(key, v, score);
            if (time > 0) redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return true;
        } catch (Throwable t) {
            logger.error("缓存[" + key + "]失败, value[" + v + "]", t);
        }
        return false;
    }

    /**
     * 缓存zSet操作
     *
     * @param PREFIX
     * @param k
     * @param v
     * @param score
     * @return
     */
    public boolean cacheZSet(String PREFIX, String k, String v, double score) {
        return cacheZSet(PREFIX, k, v, score, -1);
    }

    /**
     * 获取缓存zSet数据
     *
     * @param PREFIX
     * @param k
     * @param start
     * @param end
     * @return
     */
    public Set<String> getZSet(String PREFIX, String k, long start, long end) {
        String key = PREFIX + k;
        try {
            ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
            return zSetOps.rangeByScore(key, start, end);
        } catch (Throwable t) {
            logger.error("获取zSet缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 获取缓存zSet数据
     *
     * @param k
     * @return
     */
    public Set<String> getZSet(String PREFIX, String k, double min, double max, long offset, long count) {
        String key = PREFIX + k;
        try {
            ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
            return zSetOps.reverseRangeByScore(key, min, max, offset, count);
        } catch (Throwable t) {
            logger.error("获取zSet缓存失败key[" + PREFIX + k + ", error[" + t + "]");
        }
        return null;
    }

    /**
     * 获取缓存zSet数据总数
     *
     * @param PREFIX
     * @param k
     * @param min
     * @param max
     * @return
     */
    public long getZSetCount(String PREFIX, String k, double min, double max) {
        String key = PREFIX + k;
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
        return zSetOps.count(key, min, max);
    }


    /**
     * 执行key
     *
     * @param key
     * @return
     */
    public Set keys(String key) {
        return redisTemplate.keys(key);
    }

    /**
     * 设置某个缓存key失效时间，单位秒
     *
     * @param prefex
     * @param key
     * @param timeout
     * @return
     */
    public boolean expire(String prefex, String key, long timeout) {
        return redisTemplate.expire(prefex + key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 自增
     * @param prefex
     * @param key
     * @return
     */
    public long incr(String prefex, String key, long value) {
        String keys = prefex + key;
        try {
            return redisTemplate.boundValueOps(keys).increment(value);
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return 0;
    }
}
